package online.heycm.rbac.service.basic.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import online.heycm.platform.common.entity.result.Optional;
import online.heycm.platform.common.entity.result.PageResult;
import online.heycm.rbac.common.cache.InterfaceCache;
import online.heycm.rbac.common.cache.RoleCache;
import online.heycm.rbac.common.cache.RolePermitCache;
import online.heycm.rbac.common.constant.ErrorCode;
import online.heycm.rbac.common.converter.ConvertHelper;
import online.heycm.rbac.common.converter.RbacConverter;
import online.heycm.rbac.common.entity.RolePermitDef;
import online.heycm.rbac.common.utils.PageHelper;
import online.heycm.rbac.domain.basic.repository.RbacRepository;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacInterface;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacResource;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacRole;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacRoleInterface;
import online.heycm.rbac.domain.basic.repository.entity.BasicRbacRoleResource;
import online.heycm.rbac.param.basic.InterfaceAddReq;
import online.heycm.rbac.param.basic.InterfaceEditReq;
import online.heycm.rbac.param.basic.InterfaceListReq;
import online.heycm.rbac.param.basic.InterfacePageReq;
import online.heycm.rbac.param.basic.InterfaceRes;
import online.heycm.rbac.param.basic.ResourceAddReq;
import online.heycm.rbac.param.basic.ResourceEditReq;
import online.heycm.rbac.param.basic.ResourceListReq;
import online.heycm.rbac.param.basic.ResourcePageReq;
import online.heycm.rbac.param.basic.ResourceRes;
import online.heycm.rbac.param.basic.RoleAddReq;
import online.heycm.rbac.param.basic.RoleEditReq;
import online.heycm.rbac.param.basic.RoleInterfaceAddReq;
import online.heycm.rbac.param.basic.RoleRes;
import online.heycm.rbac.param.basic.RoleResourceAddReq;
import online.heycm.rbac.service.basic.RbacService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author hey
 * @version 1.0
 * @date 2024/1/17 15:12
 */
@Service
public class RbacServiceImpl implements RbacService {

    @Autowired
    private RbacRepository rbacRepository;

    @Override
    public Optional addRole(RoleAddReq req) {
        BasicRbacRole role = RbacConverter.INST.convert(req);
        Optional optional = rbacRepository.save(role);
        if (optional.isPresent()) {
            RoleCache.update(role);
        }
        return optional;
    }

    @Override
    public Optional editRole(RoleEditReq req) {
        BasicRbacRole role = RbacConverter.INST.convert(req);
        boolean save = rbacRepository.updateById(role);
        if (save) {
            RoleCache.update(rbacRepository.getRoleById(role.getRoleId()));
        }
        return Optional.ifFail(save, ErrorCode.ROLE_UPDATE);
    }

    @Override
    public Optional<List<RoleRes>> listRole() {
        List<BasicRbacRole> list = rbacRepository.allRoles();
        List<RoleRes> convert = ConvertHelper.list(list, RbacConverter.INST::convert);
        return Optional.of(convert);
    }

    @Override
    public Optional addInterface(InterfaceAddReq req) {
        BasicRbacInterface rbacInterface = RbacConverter.INST.convert(req);
        boolean ok = rbacRepository.save(rbacInterface);
        if (ok) {
            InterfaceCache.update(RbacConverter.INST.convertDef(rbacInterface));
        }
        return Optional.ifFail(ok, ErrorCode.INTERFACE_INSERT);
    }

    @Override
    public Optional editInterface(InterfaceEditReq req) {
        BasicRbacInterface rbacInterface = RbacConverter.INST.convert(req);
        boolean ok = rbacRepository.updateById(rbacInterface);
        if (ok) {
            InterfaceCache.update(RbacConverter.INST.convertDef(rbacInterface));
        }
        return Optional.ifFail(ok, ErrorCode.INTERFACE_UPDATE);
    }

    @Override
    public Optional<List<InterfaceRes>> listInterface(InterfaceListReq req) {
        LambdaQueryWrapper<BasicRbacInterface> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.hasText(req.getInterfacePath()), BasicRbacInterface::getInterfacePath, req.getInterfacePath());
        wrapper.eq(req.getNeedToken() != null, BasicRbacInterface::getNeedToken, req.getNeedToken());
        wrapper.eq(StringUtils.hasText(req.getRemark()), BasicRbacInterface::getRemark, req.getRemark());
        List<BasicRbacInterface> interfaces = rbacRepository.listInterface(wrapper);
        return Optional.of(ConvertHelper.list(interfaces, RbacConverter.INST::convert));
    }

    @Override
    public Optional<PageResult<InterfaceRes>> pageInterface(InterfacePageReq req) {
        IPage<BasicRbacInterface> page = PageHelper.getPage(req);
        LambdaQueryWrapper<BasicRbacInterface> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.hasText(req.getInterfacePath()), BasicRbacInterface::getInterfacePath, req.getInterfacePath());
        wrapper.eq(req.getNeedToken() != null, BasicRbacInterface::getNeedToken, req.getNeedToken());
        wrapper.eq(StringUtils.hasText(req.getRemark()), BasicRbacInterface::getRemark, req.getRemark());
        rbacRepository.pageInterface(page, wrapper);
        return Optional.of(ConvertHelper.page(page, RbacConverter.INST::convert));
    }

    @Override
    public Optional<List<Integer>> listRoleInterface(Integer roleId) {
        List<Integer> interfaces = rbacRepository.listRoleResources(roleId);
        return Optional.of(interfaces);
    }

    @Override
    public Optional addRoleInterface(RoleInterfaceAddReq req) {
        // 取角色缓存
        BasicRbacRole role = RoleCache.getById(req.getRoleId());
        // 更新权限落库
        List<BasicRbacRoleInterface> roleInterfaces = req.getInterfaceIds().stream().map(e -> {
            BasicRbacRoleInterface roleInterface = new BasicRbacRoleInterface();
            roleInterface.setRoleId(req.getRoleId());
            roleInterface.setInterfaceId(e);
            return roleInterface;
        }).collect(Collectors.toList());
        rbacRepository.saveBatchRoleInterface(roleInterfaces);
        // 刷新权限缓存
        List<RolePermitDef> permits = roleInterfaces.stream().map(e -> {
            RolePermitDef def = new RolePermitDef();
            def.setRoleId(e.getRoleId());
            def.setInterfaceId(e.getInterfaceId());
            return def;
        }).collect(Collectors.toList());
        RolePermitCache.update(role.getRoleEn(), permits);
        return Optional.success();
    }

    @Override
    public Optional addResource(ResourceAddReq req) {
        BasicRbacResource resource = RbacConverter.INST.convert(req);
        boolean ok = rbacRepository.save(resource);
        return Optional.ifFail(ok, ErrorCode.RESOURCE_INSERT);
    }

    @Override
    public Optional editResource(ResourceEditReq req) {
        BasicRbacResource resource = RbacConverter.INST.convert(req);
        boolean ok = rbacRepository.updateById(resource);
        return Optional.ifFail(ok, ErrorCode.RESOURCE_UPDATE);
    }

    @Override
    public Optional<List<ResourceRes>> listResource(ResourceListReq req) {
        LambdaQueryWrapper<BasicRbacResource> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(req.getPid() != null, BasicRbacResource::getPid, req.getPid());
        wrapper.eq(req.getResourceType() != null, BasicRbacResource::getResourceType, req.getResourceType());
        wrapper.eq(StringUtils.hasText(req.getResourceName()), BasicRbacResource::getResourceName, req.getResourceName());
        wrapper.eq(StringUtils.hasText(req.getResourceValue()), BasicRbacResource::getResourceValue, req.getResourceValue());
        wrapper.eq(StringUtils.hasText(req.getIcon()), BasicRbacResource::getIcon, req.getIcon());
        wrapper.eq(StringUtils.hasText(req.getMetadata()), BasicRbacResource::getMetadata, req.getMetadata());
        wrapper.eq(req.getSts() != null, BasicRbacResource::getSts, req.getSts());
        wrapper.orderByAsc(BasicRbacResource::getResourceType, BasicRbacResource::getPid, BasicRbacResource::getSort);
        List<BasicRbacResource> resources = rbacRepository.listResource(wrapper);
        return Optional.of(ConvertHelper.list(resources, RbacConverter.INST::convertResourceRes));
    }

    @Override
    public Optional<PageResult<ResourceRes>> pageResource(ResourcePageReq req) {
        IPage<BasicRbacResource> page = PageHelper.getPage(req);
        LambdaQueryWrapper<BasicRbacResource> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(req.getPid() != null, BasicRbacResource::getPid, req.getPid());
        wrapper.eq(req.getResourceType() != null, BasicRbacResource::getResourceType, req.getResourceType());
        wrapper.eq(StringUtils.hasText(req.getResourceName()), BasicRbacResource::getResourceName, req.getResourceName());
        wrapper.eq(StringUtils.hasText(req.getResourceValue()), BasicRbacResource::getResourceValue, req.getResourceValue());
        wrapper.eq(StringUtils.hasText(req.getIcon()), BasicRbacResource::getIcon, req.getIcon());
        wrapper.eq(StringUtils.hasText(req.getMetadata()), BasicRbacResource::getMetadata, req.getMetadata());
        wrapper.eq(req.getSts() != null, BasicRbacResource::getSts, req.getSts());
        wrapper.orderByAsc(BasicRbacResource::getResourceType, BasicRbacResource::getPid, BasicRbacResource::getSort);
        rbacRepository.pageResource(page, wrapper);
        return Optional.of(ConvertHelper.page(page, RbacConverter.INST::convertResourceRes));
    }

    @Override
    public Optional<List<ResourceRes>> treeResource() {
        LambdaQueryWrapper<BasicRbacResource> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByAsc(BasicRbacResource::getResourceType, BasicRbacResource::getPid, BasicRbacResource::getSort);
        List<BasicRbacResource> resources = rbacRepository.listResource(wrapper);
        List<BasicRbacResource> parent = resources.stream().filter(e -> e.getPid().equals(0)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(parent)) {
            return Optional.of(Collections.emptyList());
        }
        List<ResourceRes> convertParent = ConvertHelper.list(parent, RbacConverter.INST::convertResourceRes);
        deepFind(convertParent, resources);
        return Optional.of(convertParent);
    }

    private void deepFind(List<ResourceRes> parent, List<BasicRbacResource> resources) {
        for (ResourceRes p : parent) {
            List<BasicRbacResource> children = resources.stream().filter(e -> e.getPid().equals(p.getResourceId())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(children)) {
                p.setChildren(ConvertHelper.list(children, RbacConverter.INST::convertResourceRes));
                deepFind(p.getChildren(), resources);
            } else {
                p.setChildren(Collections.emptyList());
            }
        }
    }

    @Override
    public Optional<List<Integer>> listRoleResource(Integer roleId) {
        List<Integer> resources = rbacRepository.listRoleResource(roleId);
        return Optional.of(resources);
    }

    @Override
    public Optional addRoleResource(RoleResourceAddReq req) {
        List<BasicRbacRoleResource> roleResources = req.getResourceIds().stream().map(e -> {
            BasicRbacRoleResource rr = new BasicRbacRoleResource();
            rr.setRoleId(req.getRoleId());
            rr.setResourceId(e);
            return rr;
        }).collect(Collectors.toList());
        rbacRepository.saveBatchRoleResource(roleResources);
        return Optional.success();
    }
}
